Design specification · build-ready · Pipeline · Communications
pipeline.riversync.com / Communications. The exhaustive design specification for RiverSync's unified multi-channel inbox: layout grid, every panel and state, the data it renders, the realtime push that keeps it live, and the design-system mapping for every element. Written so an implementer needs no further decisions — each measurement, class, icon, enum and event is named.
The communications system is a single threaded inbox that centralizes every external touchpoint with a contact, across seven channels, into one place a sales user works without leaving Pipeline. Each conversation is one channel thread tied to the contact, customer or partner it came from, and to the leads, opportunities and deals that relationship drives — so a user moves in one click from a message to the relationship and its activity.
Seven inbound/outbound channels collapse into one queue, organized across six folders. The same single page renders every folder; the channel is the reply path, the folder is a soft, reversible placement.
Entitlement: RiverSync tenant, sales or admin role (master AUTH-2; Pipeline is RiverSync-only). Support · accounting · engineer are not entitled and never see the app. The five W's of every conversation — who (contact), where from (channel), about what (subject + linked funnel record), when (last message), what state (folder + read) — are all visible in the list row before the thread is even opened.
Communications is the first nav group in the Pipeline sidebar, above Sales and Relationships. The six folders are six nav items; all six resolve to one page, Inbox.html, parameterized by a query string. There is no per-folder page.
| Folder | Route | Nav id | Semantics & allowed actions |
|---|---|---|---|
| Inbox | ?folder=inbox | cm-inbox | Live inbound queue across all seven channels, tied to the contact. Actions: reply · forward · forward all · archive · junk · mark unread · delete · convert. |
| Draft | ?folder=draft | cm-draft | Unsent replies & outreach. Primary action is Send; the composer pre-fills the draft body. Actions: continue editing · discard. |
| Sent | ?folder=sent | cm-sent | Outbound history, threaded with the contact. Read-only thread; reply opens a new outbound. Actions: reply · forward · forward all · archive · junk · delete. |
| Archived | ?folder=archived | cm-archived | Resolved, out of the inbox, still attached to the relationship. No Archive command (already archived). Actions: reply · forward · forward all · junk · delete. |
| Junk | ?folder=junk | cm-junk | Spam/off-topic. Replies disabled — composer shows the junk lock. Primary action is Not junk (→ inbox). Actions: not junk · delete · empty junk. |
| Deleted | ?folder=deleted | cm-deleted | Soft-deleted, retained per policy (LIF-1). Replies disabled — restore first. Primary action is Restore (→ inbox). Actions: restore · delete forever · empty folder. |
Communications uses the shell's immersive page mode (pl-immersive on .rs-page, pl-immersive-body on <body>): the standard page padding and max-width are dropped so the inbox fills the content area edge to edge below the 40px top bar. The sidebar (248px, collapsible to 40px) and top bar are the standard shell chrome — the App pill is rs-portalbadge--pipeline, breadcrumbs read Communications › {Folder}.
Two entities, owned by the Sales service (SVC-18), defined once in SPEC-ERD-PIP. The UI never invents fields — every rendered value maps to one below. Field dictionaries are reproduced here verbatim from the ERD catalog so an implementer reads layout and schema together.
| Field | Key | Meaning & UI use |
|---|---|---|
| Id | PK | CNV-YYYY-NNNN — list-row data-id, reader anchor. |
| ContactId | FK | → Contact (party). Drives the avatar, name, org, role and the Open contact / View activity buttons. |
| Channel | web-formemaillineinstagramfacebooklinkedinphone — the channel chip + the reply path (DM-53). | |
| Folder | inboxdraftsentarchivedjunkdeleted — which queue; the soft state (DM-54). | |
| Subject | Thread title — list row line 3 and the reader subject bar. | |
| State | openclosed — a closed conversation locks the composer. | |
| RelatesToType | leadopportunitydealquotenone — kind of the linked funnel record. | |
| RelatesToId | FK | → funnel entity · 0..1. Renders the Linked chip row in the reader. |
| OwnerUser | FK | → the RiverSync sales user who owns the thread. |
| LastMessageAt | + Unread — list timestamp and the unread dot / bold weight. |
| Field | Key | Meaning & UI use |
|---|---|---|
| ConversationId | PF | Parent thread. |
| SeqNo | PF | Order within the thread — render ascending, top to bottom. |
| Direction | inboundoutbound — inbound = left bubble, outbound = right bubble (pl-msg--out) (DM-54). | |
| FromContactId | FK | → Contact · 0..1, inbound only. Outbound author is the OwnerUser. |
| Body | Markdown / text — rendered injection-safe; logged calls carry an italic note body. | |
| SentAt | Per-message timestamp in the bubble head. | |
| DeliveryState | draftsentdeliveredread — the outbound delivery tick (§10.4). | |
| Attachments | files · pictures · 0..n — each renders an attachment chip with name, size, kind, download. |
Below the shell chrome the page is a vertical stack: a full-width command bar, then a two-column mail body that fills the remaining height. The mail body is a CSS grid — a fixed-width conversation list on the left, the reader filling the rest. Both columns scroll independently; the page itself does not scroll.
| Region | Sizing | Specification | |
|---|---|---|---|
| 1 | Command bar .pl-commsbar | full width · 46px | Sticky top of the immersive page. Left: context commands (icon buttons). Right cluster: primary context action + Compose. A flex spacer separates them. |
| 2 | List column .pl-mail__list | fixed 320px* | Conversation list; scrolls independently. *Prototype renders a fluid ~38% column; production target is a fixed 320px list with the reader taking the remainder. |
| 3 | List tools .pl-mail__tools | auto · ~44px | Search input (fills) + filter button (34px). Pinned above the scrolling items. |
| 4 | Reader column .pl-mail__reader | fills | Takes all remaining width; internal vertical layout: head → subject → thread (scrolls) → composer (pinned bottom). |
| 5 | Linked chips .pl-reader__rels | auto | The conversation's funnel links (lead/opp/deal/partner) as click-through chips; hidden when none. |
| 6 | Thread .pl-reader__thread | fills · scrolls | Messages ascending; inbound left, outbound right. Auto-scrolls to newest on open / send. |
| 7 | Composer .pl-composer | auto · pinned | Channel-aware reply box; replaced by a locked notice in junk/deleted/closed. |
Grid: .pl-mail{display:grid; grid-template-columns:320px 1fr; height:calc(100% - 46px);}. Both panels min-height:0; overflow:auto so they scroll inside the grid rather than growing the page. Below 900px the list collapses to a full-width queue and selecting a conversation pushes the reader over it (master-detail), with a back affordance — see §13 responsive.
The bar is context-aware: which commands appear depends on the open folder and whether a conversation is selected. Secondary actions are icon-only buttons (.pl-cmd.pl-cmd--icon) on the left; the primary context action and Compose sit together at the right edge, sharing the filled accent style. When nothing is selected, the left side shows the hint "Select a conversation to act on it."
| Folder | Left (secondary, icon-only) | Right primary |
|---|---|---|
| Inbox | Forward · Forward all · Archive · Mark as junk · Mark unread · Delete | Reply (icon) + Compose |
| Sent | Forward · Forward all · Archive · Mark as junk · Mark unread · Delete | Reply (icon) + Compose |
| Archived | Forward · Forward all · Mark as junk · Mark unread · Delete (no Archive) | Reply (icon) + Compose |
| Draft | Continue editing · Discard draft | Send + Compose |
| Junk | Delete · Empty junk | Not junk + Compose |
| Deleted | Delete forever · Empty folder | Restore + Compose |
The primary action's act resolves from folder: junk → notjunk, deleted → restore, draft → send, else → reply. Reply is rendered icon-only (corner-down-left) to sit tight beside Compose; the others carry a label. Compose is always present and always last.
| Command | Icon | Style | Behavior |
|---|---|---|---|
| Reply | corner-down-left | primary | Focuses the composer and scrolls the thread to its end. No toast. |
| Forward | corner-down-right | icon | Opens an outbound compose forwarding the conversation on its own channel; writes an Activity; emits conversation.forwarded {Scope:conversation}. |
| Forward all | forward-all | icon | Forwards the entire thread — every Message — on the same channel; conversation.forwarded {Scope:thread}. |
| Archive | archive | icon | Moves to Archived; toast "Moved out of the inbox — still attached to the relationship." |
| Mark as junk | shield | icon | Moves to Junk; conversation.foldered {Folder:junk}. |
| Mark unread | icon | Re-flags the selected row unread (dot + bold) without leaving it. | |
| Delete | trash | icon · danger | Moves to Deleted (retained). In Deleted the label is Delete forever. |
| Not junk | inbox | primary | Junk only — returns to Inbox. |
| Restore | refresh | primary | Deleted only — returns to Inbox. |
| Send | send | primary | Draft only — sends the draft on its channel; moves the conversation to Sent. |
| Empty junk / folder | trash | danger | Clears the whole folder (retained per policy); always present in junk/deleted regardless of selection. |
| Compose | send | primary | Opens the new-message modal (§9). Always present, far right. |
The left column: a pinned tools row (search + filter) over a scrolling stack of conversation rows. The list is the queue; one row is selected at a time and reflected in the reader.
| Control | Specification |
|---|---|
| Search #mailSearch | Leading search icon, placeholder "Search messages…". Filters on input (no debounce needed at this scale) across party name, org, subject and preview — case-insensitive substring. Empties restore the full list. |
| Filter button #filterBtn | 34px square icon button (filter glyph). Opens a dropdown of facets. When any facet is active it gains is-active and a count badge (.pl-mail__filterbadge) showing the number of active facets. |
The filter menu carries four single-select facets; each is a radio group (a tick marks the active option). Facets only appear when meaningful for the folder's contents.
| Facet | Options | Notes |
|---|---|---|
| Channel | All + each present channel | Shown only if the folder holds >1 channel; options carry the channel icon. |
| Status | All · Unread · Read | Always present. |
| Attachments | Any · Has attachment | "Has attachment" carries the paperclip icon. |
| Relationship | Anyone + present kinds | Lead · Customer · Partner · Unverified — shown only if the folder holds >1 kind. |
Facets are AND-combined with search. A Clear all filters footer appears when any facet is active and resets all four to "all". The list header shows N shown (and of M when filtered). The filter dropdown closes on outside-click or Escape.
One row (.pl-conv, a <button>) packs the whole at-a-glance summary into ~70px:
| Element | Spec |
|---|---|
| Unread dot | .pl-conv__dot — accent dot, left gutter; present only when unread. Row also gets is-unread (name bold). |
| Avatar | 34px .rs-av with the contact's initials and assigned color class (rs-av-c0…c5). |
| Name + channel + time | Top line: contact name (left), then the channel chip + relative time (right). Channel chip = rs-badge + tone (§9). |
| Org + clip | Second line: organization name, plus a paperclip glyph when the thread has attachments. |
| Subject | Third line: .pl-conv__subj — the conversation subject, medium weight. |
| Preview | Fourth line: one-line snippet of the latest message, truncated; .pl-conv__prev, muted. |
| Selected | is-sel — accent-soft fill, persists while its thread is open. |
The right column is a fixed vertical layout: a contact head, a subject bar with linked chips, the scrolling thread, and a pinned composer (or lock).
| Region | Specification |
|---|---|
| Head .pl-reader__head | Avatar (34px) + name with a kind tag (Lead / Customer / Partner / Unverified, .pl-ktag--*) + role · org. Right: Open contact and View activity ghost buttons (suppressed for unverified senders), then an overflow more-horizontal menu. |
| Overflow menu | Archive (or Not junk / Restore by folder) · Convert to lead / opportunity… · separator · Delete (danger). Mirrors the command bar for reachability. |
| Subject bar .pl-reader__subj | The conversation subject (large) with the channel chip to its right. |
| Linked chips .pl-reader__rels | Label Linked + one chip per related record. Chip = icon by type (deal trend-up, opportunity layers, lead inbox, partner hub) + label + mono id; links to that record's page. Whole row hidden when none. |
| Thread .pl-reader__thread | Message blocks ascending. Inbound = left, avatar + body. Outbound = pl-msg--out, right-aligned, with a You tag. Each block: author, timestamp, markdown body, then attachment chips. |
| Attachment chip .pl-att | Kind icon (image / file-text), name (bold) + size, and a download affordance. Images open a lightbox; files download. |
The composer always sends on the conversation's own channel — there is no channel switch inside a thread. Its header states the reply verb per channel; its footer states the destination.
| Part | Spec |
|---|---|
| Bar | .pl-composer__bar — channel icon + reply verb (e.g. "Reply on LINE", "Reply by email", phone → "Log a follow-up call") on the left; Attach file (paperclip) and Add picture (image) icon buttons on the right. |
| Textarea | .pl-composer__ta — placeholder "Write a reply…" (or "Pick up your draft…" in Draft, where it pre-fills the draft body). |
| Footer | Muted note "Sends on {Channel}, threaded to {Name}." + the Send button (Send draft in Draft). |
.pl-reader__locked with the shield glyph — "This conversation is filtered as junk. Replies are disabled — mark it not junk to move it back to the inbox."
trash glyph — "This conversation is in Deleted. Restore it to reply."
State = closed → the composer is replaced by the locked note; no outbound until reopened.
Sent / Archived render the thread read-only but still offer a reply (which opens a fresh outbound on the same channel).
When the list is empty (no conversations, or none match the filter) the reader shows .pl-reader__empty: the folder's icon, "Select a conversation", and the folder's descriptive subtitle. The list itself shows .pl-mail__none — a search glyph + "No conversations match." when filtered to nothing.
Compose opens a large modal (.rs-modal.rs-modal--lg in an .rs-scrim--drawer) for net-new outreach — distinct from a thread reply, because here the recipient and channel are not yet fixed.
| Field | Spec |
|---|---|
| Header | Send-icon tile (accent-soft) + "New message" + sub "Reach a contact on any channel. The thread attaches to their relationship and activity." + close. |
| To (required) | Contact / customer / partner picker (leading user icon). Resolves to a ContactId — the thread anchors there. |
| Channel (required) | Select: Email — sales@riversync.com · LINE · Instagram · Facebook · LinkedIn · Web form reply. (Phone threads are logged calls, created from the call-log flow, not composed here.) |
| Subject | Free text — "What is this about?" |
| Message | Multi-line body — "Write your message…" |
| Reassurance | An info alert: "One thread per topic" — whichever channel, the conversation centralizes here and links to the contact. |
| Footer | Save as draft (ghost → Draft folder) · Send (primary). Send closes the modal and toasts "It's threaded to the contact and added to their activity timeline." |
One thread idiom serves all seven; only the channel chip, the reply verb and a few behaviors differ. The chip is the DS Badge (rs-badge + tone) with the channel icon + label — never a hand-rolled pill.
| Channel | Icon | Badge tone | Reply verb | Notes |
|---|---|---|---|---|
| Web form | globe | neutral | Reply by email | The riversync.com contact form — the only fully self-service capture. Reply routes to the submitter's email. |
| rs-badge--info | Reply by email | To sales@riversync.com. Threading by subject + participant. | ||
| LINE | line | rs-badge--success | Reply on LINE | Thailand's primary channel — expect high inbound volume. |
| rs-badge--accent | Reply on Instagram | DMs from the brand account; previews show @handle. | ||
| rs-badge--info | Reply on Facebook | Page messages. | ||
| rs-badge--info | Reply on LinkedIn | InMail / connection messages; B2B intros. | ||
| Phone | phone | rs-badge--success | Log a follow-up call | Logged calls — outbound-authored by the owner; body is an italic call note with duration. No live inbound push. |
Inbound capture is the integration boundary, not a UI concern: each provider's webhook/poller lands a Message and emits message.received (§11). The UI treats all seven identically once a Message exists — it never speaks a provider API directly.
Every action runs through one dispatcher keyed by an act string, fired from three places — the command bar, the composer Send, and the reader overflow menu — so a given action behaves identically wherever it is invoked.
| act | Result | Toast |
|---|---|---|
| reply | Focus composer, scroll thread to end | — (no toast) |
| send | Append outbound Message; → Sent; write Activity | "Sent · Message sent on its channel and added to the contact's activity." (success) |
| forward | Open outbound forward (conversation) | "Forward · Opens a forward of this conversation on the same channel." (info) |
| forwardall | Open outbound forward (whole thread) | "Forward all · …the entire thread — every message…" (info) |
| archive | → Archived | "Archived · Moved out of the inbox — still attached to the relationship." (info) |
| junk | → Junk | "Marked as junk · …Mark it not junk to bring it back." (warning) |
| notjunk | → Inbox | "Moved to inbox · Marked not junk…" (success) |
| restore | → Inbox | "Restored · Conversation moved back to the inbox." (info) |
| delete | → Deleted (retained) | "Deleted · Moved to Deleted, retained per policy." (warning) |
| unread | Re-flag selected row unread | "Marked unread · Flagged as unread in the list." (info) |
| empty | Clear the whole folder (retained) | "Folder emptied · …(retained per policy)." (warning) |
| convert | Open lead/opportunity capture, pre-filled | "Convert · Opens the lead / opportunity capture, pre-filled…" (info) |
Each move sets the conversation's Folder and emits conversation.foldered {ConversationId, Folder, By}. The moved conversation leaves the current list immediately; selection advances to the next visible row (or empties the reader if none remain). Reversible moves (junk ⇄ inbox, deleted → inbox) are the same mechanism in reverse.
Selecting a row clears Unread; act:unread sets it again. The sidebar folder pill and the list header count derive from the unread set and update synchronously on any change. Unread rows carry the dot and bold name; read rows do not.
List and reader reserve their exact dimensions and show skeleton rows / a quiet placeholder — layout never reflows when data arrives (mirrors the charts rule).
List → folder icon + the folder's subtitle. Reader → "Select a conversation" empty state. No error styling — an empty Junk is success.
List → search glyph + "No conversations match."; Clear-all in the filter footer resets.
One-sentence inline error + Retry; an optimistic outbound that fails its server ack rolls back its DeliveryState to a retryable error chip (§11.4, §12).
Outbound messages carry a DeliveryState that advances draftsentdeliveredread. Render a tick that fills as it advances (single → double → accent double on read), mono timestamp alongside. A failed send shows a danger state with Retry; it never silently disappears.
The inbox is live (PIP-15 v0.9): new inbound messages, delivery-state changes and folder moves are pushed to the client over a SignalR (WebSocket) hub and applied in place — no manual refresh. Local user actions update optimistically and reconcile on the server acknowledgement. The push rides the existing Sales domain events; the hub is the transport, not a new source of truth.
| Concept | Spec |
|---|---|
| Hub | /hubs/communications on the Pipeline BFF. Authenticated as the RiverSync sales/admin user; rejects unentitled roles (SVC-3 — browser talks only to its app's BFF). |
| Group | Each connection joins the group of the RiverSync sales tenant (and optionally per-owner sub-groups) so a push reaches every open Pipeline inbox. Conversations are RiverSync-internal — no customer/partner ever joins this hub. |
| Scope | The hub carries Sales communications only. Customer↔support realtime is a separate concern on the Portal/Operations side (ChatThread) — never multiplexed here. |
| Method | Payload (from event) | Client applies |
|---|---|---|
| MessageReceived | {ConversationId, MessageId, ContactId, Channel, Folder} | Append the message; if the thread is open, render & auto-scroll; else raise the row's unread dot, re-sort by LastMessageAt, bump counts, toast + bell badge. |
| MessageSent | {ConversationId, MessageId, ContactId, Channel} | Reconcile an optimistic outbound (replace temp id with server id) or append if it originated elsewhere. |
| DeliveryChanged | {ConversationId, MessageId, DeliveryState} | Advance the delivery tick in place; no reflow. |
| ConversationFoldered | {ConversationId, Folder, By} | If the conversation left the current folder, remove its row & advance selection; if it arrived, insert it. |
| ConversationForwarded | {ConversationId, ContactId, Channel, Scope, MessageId} | Append the forwarded outbound to the thread & the contact's activity. |
| Concern | Rule |
|---|---|
| Ordering | Server SeqNo is authoritative. Optimistic messages render last until reconciled, then snap to their SeqNo position. |
| Idempotency | A push for a Message already present (matched by id) is a no-op — guards against the ack + broadcast double-apply. |
| Reconnection | On reconnect, re-fetch the active folder + open thread to backfill anything missed while offline; the hub does not replay. |
| Reduced motion | New-message insert and delivery-tick changes are instant (no slide) under prefers-reduced-motion. |
The inbox is not a silo. Every message keeps the relationship's history whole, and every thread is one click from the records it concerns.
Every send and receive writes an Activity. An inbound message.received and an outbound message.sent each append an Activity (type email/call/…, the matching direction) anchored to the conversation's ContactId — so the contact's blended timeline (PIP-14, DM-52) holds the full story without the user logging anything by hand.
Linked funnel records are first-class. A conversation's RelatesToType + RelatesToId render the Linked chip row; each chip deep-links to the lead, opportunity, deal or partner. The head's Open contact and View activity jump to the contact record and its timeline.
Convert in place. The reader overflow offers Convert to lead / opportunity…, pre-filled from the conversation — turning an inbound message into a tracked pursuit without retyping (feeds Lead to opportunity).
| Concern | Requirement |
|---|---|
| Semantics | List is a role="listbox" of rows (each a <button>, aria-selected); reader is the labelled detail region. The unread dot has an aria-label="unread"; the channel chip's label is real text, not icon-only. |
| Focus | 3px soft focus ring (--ring) on :focus-visible only — rows, buttons, composer, filter options. Opening a thread moves focus to the reader head; closing the filter returns focus to the filter button. |
| Filter menu | Roving radio semantics per facet (role="radiogroup"); Escape closes and restores focus; outside-click closes. |
| Color | Channel/relationship meaning is never color-only — each chip pairs an icon + text label; delivery state pairs the tick with a tooltip word. |
| Motion | Toasts, new-message inserts and delivery ticks are instant under prefers-reduced-motion; no decorative looping. |
| Hit targets | Rows ≥ the compact 38px row; icon buttons 34px; composer Send comfortably tappable. |
| Key | Action (proposed for production; prototype is pointer-first) |
|---|---|
| ↑ ↓ | Move selection through the list. |
| Enter | Open / keep the selected thread; from the composer, Enter sends, Shift+Enter newline. |
| e / # | Archive / Delete the selected conversation. |
| r / f | Reply / Forward. |
| / | Focus search. Esc clears it / closes the filter. |
Every element resolves to a RiverSync DS v2 class, icon or token — nothing is bespoke. The inbox descends from the vanilla shell kit (rs-shell.js + riversync-ui.css); the pl-* classes are the Pipeline-local inbox layer in pipeline.css, themed entirely through the shell's CSS variables.
| Element | Class | DS basis |
|---|---|---|
| App pill (top bar) | rs-portalbadge--pipeline --sm | Canonical app badge, Pipeline gold hue (shell-chrome.css). |
| Channel chip | rs-badge + tone | DS Badge — 2px radius, soft tint, sentence case (badge.card.html). |
| Relationship kind tag | pl-ktag--lead/customer/partner/prospect | Pipeline-local status tag, DS tone palette. |
| Avatar | rs-av rs-av-c0…c5 | DS avatar tile (square, initials). |
| Buttons | rs-btn rs-btn--primary/--ghost/--sm, rs-iconbtn | DS button kit + accent press behavior. |
| Command buttons | pl-cmd pl-cmd--primary/--danger/--icon | Inbox command-bar buttons over DS tokens. |
| Inputs / select | rs-input rs-input-wrap rs-select | DS forms; search leading icon via rs-ic-lead. |
| Filter menu | pl-mail__filtermenu pl-fm__* | Pipeline-local dropdown, DS menu styling. |
| Modal / scrim | rs-modal rs-modal--lg rs-scrim--drawer | DS overlay (8px rise + scale, shadow-lg). |
| Alert | rs-alert rs-alert--info | DS inline alert. |
| Toast | RS.toast({title,msg,kind}) | DS toast — success/info/warning tones. |
| Icons | data-ic="…" + RS.hydrateIcons() | Proprietary icon system (the G geometry table) — never a third-party set. |
Icons in use: inbox · edit · send · archive · shield · trash · search · filter · paperclip · image · file-text · download · email · phone · globe · line · instagram · facebook · linkedin · user · activity · check · refresh · corner-down-left · corner-down-right · forward-all · more-horizontal · x · trend-up · layers · hub · info. Any new glyph is added to the DS G table first, never drawn inline.
The prototype is the source of truth for look & behavior; these are the deltas a production build must close. None changes the model.
| Area | As-built | Production target |
|---|---|---|
| List width | fluid ~38% | Fixed 320px list, reader fills remainder. |
| Realtime | static dataset | Live SignalR hub + optimistic reconcile (§12). |
| Folder moves | toast only | Actually move the conversation & emit conversation.foldered. |
| Compose / reply | toast only | Persist Message, write Activity, emit message.sent. |
| Attachments | chips only | Real upload (staged chips + progress), image lightbox, file download. |
| Keyboard | pointer-first | Full keyboard model (§14). |
| Responsive | two-pane | <900px master-detail with back affordance. |
| This spec | Traces to |
|---|---|
| Whole document | PIP-15 (Pipeline PRD) realizing SAL-9 (platform). |
| Data model (§4) | Conversation · Message · DM-53 · DM-54 (SPEC-ERD-PIP). |
| Realtime (§12) | message.received · message.sent · conversation.foldered · conversation.forwarded · SVC-18 (SPEC-DDD-SAL). |
| Process (§11, §13) | SPEC-PWF-COM · Conversation lifecycle in SPEC-DWF-SAL. |
| Activity (§13) | PIP-14 · DM-51 · DM-52 (blended timeline). |
| Version | Date | Changes |
|---|---|---|
| 1.0 | 28 Jun 2026 | First issue — the comprehensive Communications design specification: scope & distinctions (§1–2), information architecture & routing (§3), the Conversation/Message data the UI renders (§4), layout & grid with annotated wireframe (§5), command bar (§6), list panel — search/filter/row anatomy (§7), reader & composer (§8), compose modal (§9), the seven channels (§10), interactions & states incl. delivery states (§11), the SignalR realtime contract with optimistic reconcile (§12), activity logging & cross-links (§13), accessibility/keyboard/motion (§14), design-system mapping (§15), as-built vs target, open questions, traceability (§16–18). A presentation view over a settled model — no new entities, fields, events or rules; cross-referenced from SPEC-APP-PIP. New Design specs doc family (SPEC-UX). |